package org.streets.commons.expression;

import java.util.ArrayList;
import java.util.List;

/**
 * 逻辑表达式解析器
 * 
 * <code>
 * <xmp>
 * 作用：
 * 		用于内嵌逻辑表达式，如
 * 				权限值大于普通用户，且不是管理员。
 * 					user>normal&&user!=admin"
 *      其中normal和admin为内置的角色
 * 
 * 操作数及操作类型：
 *		1)数值(整型、浮点型): == > >= < <= <>/!=
 *		2)boolean: && || !
 *		java对象：Express、boolean、String、int/float
 * 
 * 逻辑表达式文法：
 *  	expx -> exp {op expx}
 * 		exp ->  tag | "(" expx ")" | op expx
 * 		op -> == | > | >= | < | <= | <> | != | && | || | !
 * 		tag -> boolean | number | {text}+
 *  	boolean -> true | false
 *  	number -> [0-9]+(.[0-9]+)? 
 *  	text -> charset
 *  
 *  expx与exp使用同一java对象，文法抽出来的原因：1)支持连续表达式 2)避免左递归  
 *  </xmp>
 *  </code>
 *  
 *  @author dzb
 */

public class ExpressParser {
	List<String> tokens = new ArrayList<String>();
	private Lexer lex;
	private int i;

	public Express parse(String expr) {
		lex = new Lexer(expr);
		lex.delSpecialChar('.');//用于数值
		tokens.clear();
		StringBuffer token = new StringBuffer();
		while (!lex.eof()) {
			String t = lex.nextToken().trim();
			if (t.length() == 1 && mayBeComposite(t.charAt(0))) {
				token.append(t);
			} else {
				if (token.toString().length() > 0) {
					tokens.add(token.toString());
					token.setLength(0);
				}
				if (t.length() > 0) {
					tokens.add(t);
				}
			}
		}
		System.out.println(tokens);
		// build tree
		reset();
		Express root = expx();
		return root;
	}

	/**
	 * 可能组合的特殊字符，如：&&,>=...
	 */
	private boolean mayBeComposite(char ch) {
		return lex.isSpecialChar(ch) && (ch != '(' && ch != ')');
	}

	private String next() {
		return tokens.get(i++);
	}

	private void reset() {
		i = 0;
	}

	private void back() {
		i--;
	}

	private boolean eof() {
		return i == tokens.size();
	}

	private void match(String expect, String t) {
		if (!expect.equals(t)) {
			throw new ParseException("期望字符串" + expect + ":" + t);
		}
	}

	private boolean isOp(String t) {
		char ch = t.charAt(0);
		return ch != '(' && ch != ')' && lex.isSpecialChar(ch);
	}

	private Express expx() {
		Express e = exp();
		if (!eof()) {
			String t = next();
			if (isOp(t)) {
				e.setOp(op(t));
				e.setOpExp(expx());
			} else {
				back();
			}
		}
		return e;
	}

	private Express exp() {
		if (eof()) {
			return null;
		}
		String t = next();
		if (t.equals("(")) {
			Express e = new Express(expx());
			match(")", next());
			return e;
		} else if (isOp(t)) {
			Express e = new Express("");
			e.setOp(op(t));
			e.setOpExp(expx());
			return e;
		} else {
			return new Express(tag(t));
		}
	}

	private Operation op(String t) {
		return new Operation(t);
	}

	private Object tag(String t) {
		String b = t.toLowerCase();
		if (b.equals("true") || b.equals("false")) {
			return b.equals("true");
		}
		if(t.matches("[0-9]+(\\.[0-9]+)?")){
			return Float.parseFloat(t);
		}
		return t;
	}
}
